ITT-196 review_followup source-of-truth를 .github에 패키징#14
Conversation
There was a problem hiding this comment.
Code Review
이 풀 리퀘스트는 Hermes PR 리뷰 게이트를 위한 공유 도구들을 추가하며, 런타임 복사본 동기화 스크립트, 운영 런북, 웹훅 프롬프트 템플릿 및 리뷰어 로스터 스키마 등을 포함합니다. 리뷰어는 sync_runtime_copy.py에서 runtime_dir을 절대 경로로 변환하여 파일 참조 오류를 방지할 것과, 누락된 multica_agent_guard.py 파일을 복사 대상에 추가할 것을 제안했습니다. 또한, 이식성을 위해 런북과 프롬프트 내의 python3.11 명령어를 일반적인 python3로 변경할 것과 reviewer_roster.schema.json의 $id 경로 불일치 문제를 해결할 것을 권장했습니다.
| def main() -> int: | ||
| args = parse_args() | ||
| source_dir = Path(__file__).resolve().parent | ||
| runtime_dir = Path(args.runtime_dir).expanduser() |
| "RUNTIME_TOOLS_DIR": str(runtime_dir), | ||
| "REVIEW_FOLLOWUP_PATH": str(runtime_dir / "review_followup.py"), | ||
| "REVIEWER_ROSTER_PATH": str(runtime_dir / "reviewer_roster.json"), | ||
| "MULTICA_GUARD_PATH": str(runtime_dir / "multica_agent_guard.py"), |
| 2. `python3.11 {{REVIEW_FOLLOWUP_PATH}} <ISSUE> --review-pack --post-comment`로 리뷰 요청 pack을 남긴다. | ||
| 3. Claude/Codex는 high-signal reviewer, Gemini/Copilot은 supplementary reviewer로 리뷰를 수집한다. | ||
| 4. 리뷰 결과는 `must-fix`, `should-fix`, `question`, `non-actionable` 섹션으로 받는다. | ||
| 5. `python3.11 {{REVIEW_FOLLOWUP_PATH}} <ISSUE> --post-comment`로 PR 상태, checks, reviewThreads, 리뷰 triage, approval gate를 수집한다. |
There was a problem hiding this comment.
특정 Python 버전(python3.11)에 의존하는 대신 일반적인 python3 명령어를 사용하는 것이 이식성 측면에서 좋습니다. review_followup.py의 shebang도 python3를 사용하고 있습니다.
| 2. `python3.11 {{REVIEW_FOLLOWUP_PATH}} <ISSUE> --review-pack --post-comment`로 리뷰 요청 pack을 남긴다. | |
| 3. Claude/Codex는 high-signal reviewer, Gemini/Copilot은 supplementary reviewer로 리뷰를 수집한다. | |
| 4. 리뷰 결과는 `must-fix`, `should-fix`, `question`, `non-actionable` 섹션으로 받는다. | |
| 5. `python3.11 {{REVIEW_FOLLOWUP_PATH}} <ISSUE> --post-comment`로 PR 상태, checks, reviewThreads, 리뷰 triage, approval gate를 수집한다. | |
| 2. python3 {{REVIEW_FOLLOWUP_PATH}} <ISSUE> --review-pack --post-comment로 리뷰 요청 pack을 남긴다. | |
| 3. Claude/Codex는 high-signal reviewer, Gemini/Copilot은 supplementary reviewer로 리뷰를 수집한다. | |
| 4. 리뷰 결과는 must-fix, should-fix, question, non-actionable 섹션으로 받는다. | |
| 5. python3 {{REVIEW_FOLLOWUP_PATH}} <ISSUE> --post-comment로 PR 상태, checks, reviewThreads, 리뷰 triage, approval gate를 수집한다. |
| python3.11 {{REVIEW_FOLLOWUP_PATH}} --scan-status in_review --output json | ||
| python3.11 {{REVIEW_FOLLOWUP_PATH}} ITT-102 --review-pack --post-comment | ||
| python3.11 {{REVIEW_FOLLOWUP_PATH}} ITT-102 --pr-url https://github.com/owner/repo/pull/123 --post-comment | ||
| python3.11 {{REVIEW_FOLLOWUP_PATH}} --resolve-pr-url https://github.com/owner/repo/pull/123 --statuses in_review,in_progress,blocked,todo --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --create-triage-issue-on-miss --output json |
There was a problem hiding this comment.
위와 동일하게 python3.11 대신 python3를 사용하는 것을 권장합니다.
| python3.11 {{REVIEW_FOLLOWUP_PATH}} --scan-status in_review --output json | |
| python3.11 {{REVIEW_FOLLOWUP_PATH}} ITT-102 --review-pack --post-comment | |
| python3.11 {{REVIEW_FOLLOWUP_PATH}} ITT-102 --pr-url https://github.com/owner/repo/pull/123 --post-comment | |
| python3.11 {{REVIEW_FOLLOWUP_PATH}} --resolve-pr-url https://github.com/owner/repo/pull/123 --statuses in_review,in_progress,blocked,todo --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --create-triage-issue-on-miss --output json | |
| python3 {{REVIEW_FOLLOWUP_PATH}} --scan-status in_review --output json | |
| python3 {{REVIEW_FOLLOWUP_PATH}} ITT-102 --review-pack --post-comment | |
| python3 {{REVIEW_FOLLOWUP_PATH}} ITT-102 --pr-url https://github.com/owner/repo/pull/123 --post-comment | |
| python3 {{REVIEW_FOLLOWUP_PATH}} --resolve-pr-url https://github.com/owner/repo/pull/123 --statuses in_review,in_progress,blocked,todo --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --create-triage-issue-on-miss --output json |
| Task: | ||
| 1. Determine the canonical PR URL. Prefer `{pull_request.html_url}`. If that is empty and `{issue.html_url}` is a pull request conversation URL, use that instead. | ||
| 2. If `{action}` is `closed` and `{pull_request.merged}` resolves to `true`, treat this as merge aftercare instead of review-gate follow-up: | ||
| - Run `python3.11 {{REVIEW_FOLLOWUP_PATH}} --merged-aftercare-pr-url <PR_URL> --statuses done,in_review,in_progress,blocked,todo,backlog --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --create-triage-issue-on-miss --fallback-project-title {{FALLBACK_PROJECT_TITLE}} --event-action "{action}" --sender "{sender.login}" --head-sha "{pull_request.head.sha}" --head-ref "{pull_request.head.ref}" --base-ref "{pull_request.base.ref}" --pr-merged "{pull_request.merged}" --merge-commit-sha "{pull_request.merge_commit_sha}" --merged-at "{pull_request.merged_at}" --merged-by "{pull_request.merged_by.login}" --apply-aftercare --output json`. |
There was a problem hiding this comment.
프롬프트 내의 실행 예시에서도 python3.11 대신 python3를 사용하는 것이 범용적입니다.
- Run python3 {{REVIEW_FOLLOWUP_PATH}} --merged-aftercare-pr-url <PR_URL> --statuses done,in_review,in_progress,blocked,todo,backlog --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --create-triage-issue-on-miss --fallback-project-title {{FALLBACK_PROJECT_TITLE}} --event-action "{action}" --sender "{sender.login}" --head-sha "{pull_request.head.sha}" --head-ref "{pull_request.head.ref}" --base-ref "{pull_request.base.ref}" --pr-merged "{pull_request.merged}" --merge-commit-sha "{pull_request.merge_commit_sha}" --merged-at "{pull_request.merged_at}" --merged-by "{pull_request.merged_by.login}" --apply-aftercare --output json.
| - If `aftercare.state` is `blocked`, return `blocked` with the ambiguous families or warnings instead of guessing. | ||
| - Do not run the review-gate flow below for merged PRs. | ||
| 3. If this is an `issue_comment` payload without a pull request URL, stop and return `blocked` with the reason `non-pr-issue-comment`. | ||
| 4. Run `python3.11 {{REVIEW_FOLLOWUP_PATH}} --resolve-pr-url <PR_URL> --statuses in_review,in_progress,blocked,todo --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --create-triage-issue-on-miss --fallback-project-title {{FALLBACK_PROJECT_TITLE}} --event-name "{event}" --event-action "{action}" --sender "{sender.login}" --review-author "{review.user.login}" --review-id "{review.id}" --review-state "{review.state}" --review-body "{review.body}" --review-url "{review.html_url}" --comment-author "{comment.user.login}" --comment-id "{comment.id}" --comment-body "{comment.body}" --comment-url "{comment.html_url}" --head-sha "{pull_request.head.sha}" --output json`. |
There was a problem hiding this comment.
python3.11 대신 python3를 사용하도록 수정합니다.
4. Run python3 {{REVIEW_FOLLOWUP_PATH}} --resolve-pr-url <PR_URL> --statuses in_review,in_progress,blocked,todo --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --create-triage-issue-on-miss --fallback-project-title {{FALLBACK_PROJECT_TITLE}} --event-name "{event}" --event-action "{action}" --sender "{sender.login}" --review-author "{review.user.login}" --review-id "{review.id}" --review-state "{review.state}" --review-body "{review.body}" --review-url "{review.html_url}" --comment-author "{comment.user.login}" --comment-id "{comment.id}" --comment-body "{comment.body}" --comment-url "{comment.html_url}" --head-sha "{pull_request.head.sha}" --output json.
| 6. If `resolution.state == needs-triage` and `created_issue` exists, stop there. The resolver already left a visible Multica tracking issue, so do not post an extra comment elsewhere. | ||
| 7. If `resolution.state == ignored-external-repo`, stop there and return `external-repo-skipped`. Unlinked fallback tracking applies only to `owner == ittae` repositories. | ||
| 8. If more than one linked Multica issue matches the same PR, return `blocked` and list the candidates instead of guessing. | ||
| 9. If exactly one linked issue exists, run `python3.11 {{REVIEW_FOLLOWUP_PATH}} <ISSUE_IDENTIFIER> --pr-url <PR_URL> --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --event-name "{event}" --event-action "{action}" --sender "{sender.login}" --review-author "{review.user.login}" --review-id "{review.id}" --review-state "{review.state}" --review-body "{review.body}" --review-url "{review.html_url}" --comment-author "{comment.user.login}" --comment-id "{comment.id}" --comment-body "{comment.body}" --comment-url "{comment.html_url}" --head-sha "{pull_request.head.sha}" --stabilize-seconds 5 --stabilize-attempts 2 --output json`. |
There was a problem hiding this comment.
python3.11 대신 python3를 사용하도록 수정합니다.
9. If exactly one linked issue exists, run python3 {{REVIEW_FOLLOWUP_PATH}} <ISSUE_IDENTIFIER> --pr-url <PR_URL> --reviewer-roster-file {{REVIEWER_ROSTER_PATH}} --event-name "{event}" --event-action "{action}" --sender "{sender.login}" --review-author "{review.user.login}" --review-id "{review.id}" --review-state "{review.state}" --review-body "{review.body}" --review-url "{review.html_url}" --comment-author "{comment.user.login}" --comment-id "{comment.id}" --comment-body "{comment.body}" --comment-url "{comment.html_url}" --head-sha "{pull_request.head.sha}" --stabilize-seconds 5 --stabilize-attempts 2 --output json.
| @@ -0,0 +1,74 @@ | |||
| { | |||
| "$schema": "https://json-schema.org/draft/2020-12/schema", | |||
| "$id": "https://github.com/ittae/.github/tools/review_followup/reviewer_roster.schema.json", | |||
There was a problem hiding this comment.
$id의 경로(https://github.com/ittae/.github/tools/...)와 실제 파일 경로(tools/...)가 일치하지 않습니다. 또한 PR 설명의 검증 명령어들이 .github/tools/ 경로를 사용하고 있는 것으로 보아, 파일들이 .github/ 디렉토리 아래로 이동되어야 하는지 확인이 필요합니다.
There was a problem hiding this comment.
Pull request overview
Hermes의 PR 리뷰 게이트 도구(review_followup)를 tools/review_followup/ 아래 “공개 source-of-truth”로 정리하고, 런타임 디렉터리로 동기화하는 설치 스크립트/템플릿 문서/roster 스키마 및 회귀 테스트를 추가한 PR입니다.
Changes:
tools/review_followup/에 runbook/webhook prompt/roster schema+example/runtime sync 스크립트/테스트를 추가- GitHub webhook 기반 follow-up/merged aftercare 플로우를 문서화하고(템플릿 치환 포함) 런타임 복사본 생성 경계를 명확화
- repo-local unittest 기반 회귀 테스트 2종 추가 및 루트 README에 tooling 링크 추가
Reviewed changes
Copilot reviewed 9 out of 11 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tools/review_followup/tests/test_review_followup.py | follow-up policy/notification 로직 회귀 테스트 추가 |
| tools/review_followup/tests/test_review_followup_runtime.py | 문서/CLI 엔트리포인트/merged-aftercare 경로 포함 런타임 스모크 테스트 추가 |
| tools/review_followup/sync_runtime_copy.py | source-of-truth → 런타임 디렉터리 동기화(템플릿 렌더링 포함) 스크립트 추가 |
| tools/review_followup/reviewer_roster.schema.json | reviewer roster JSON schema 추가 |
| tools/review_followup/reviewer_roster.example.json | 런타임 bootstrap 용 example roster 추가 |
| tools/review_followup/review_followup_webhook_prompt.txt | webhook prompt 템플릿 추가(merge aftercare + follow-up 분기 포함) |
| tools/review_followup/review_followup_runbook.md | 운영 runbook 템플릿 추가(경계/게이트/알림/aftercare 정책 포함) |
| tools/review_followup/README.md | 디렉터리 경계/컨텐츠/동기화/테스트 방법 문서 추가 |
| README.md | shared tooling 링크 추가 |
| .gitignore | Python 캐시/바이트코드 무시 규칙 추가 |
| "role": { | ||
| "type": "string", | ||
| "enum": ["required", "supplementary"] | ||
| }, | ||
| "availability": { | ||
| "type": "string", | ||
| "enum": ["active", "inactive"] | ||
| }, | ||
| "legacy_names": { | ||
| "type": "array", | ||
| "items": { | ||
| "type": "string", | ||
| "minLength": 1 | ||
| } | ||
| }, | ||
| "signal_source": { | ||
| "oneOf": [ | ||
| { | ||
| "type": "string", | ||
| "enum": ["multica", "github-review"] | ||
| }, | ||
| { | ||
| "type": "array", | ||
| "minItems": 1, | ||
| "uniqueItems": true, | ||
| "items": { | ||
| "type": "string", | ||
| "enum": ["multica", "github-review"] | ||
| } | ||
| } | ||
| ] | ||
| }, |
| def write_roster(payload: dict) -> str: | ||
| handle = tempfile.NamedTemporaryFile("w", suffix=".json", delete=False) | ||
| with handle: | ||
| json.dump(payload, handle) | ||
| return handle.name |
| runbook = RUNBOOK_PATH.read_text() | ||
| prompt = PROMPT_PATH.read_text() |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: d6c9d69974
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @patch.object(review_followup, "gh_review_threads", return_value=([], None)) | ||
| @patch.object(review_followup, "gh_pr_checks", return_value=([{"name": "CI", "state": "SUCCESS"}], None)) | ||
| @patch.object(review_followup, "gh_repo_default_branch", return_value=("main", None)) |
There was a problem hiding this comment.
Mock all GitHub CLI calls in this unit test
test_main_accepts_pr_423_claude_github_signal patches gh_pr_view, gh_pr_checks, and gh_review_threads, but main() now also reaches gh_pr_reviews, gh_issue_comments, and gh_commit_details via collect_issue_follow_up_context. In environments without gh installed or authenticated, this test fails with a subprocess error and becomes nondeterministic instead of hermetic. Please patch the remaining GitHub-call helpers (or inject a fake command runner) so the test does not depend on external CLI/network state.
Useful? React with 👍 / 👎.
🌷 Summary
tools/review_followup/를 Hermes PR review gate tooling의 public source-of-truth로 추가했습니다.~/.hermes/workspace/tools로 렌더/복사하는sync_runtime_copy.py를 추가했습니다.needs-triage라벨 처리와 runtime path 참조를 정리했습니다.📢 Description
이번 변경은
review_followup.py, runbook/prompt template,reviewer_rosterschema/example, repo-local regression tests를ittae/.github안에서 PR 가능한 공용 원본으로 관리하기 위한 패키징입니다.핵심 변경점:
tools/review_followup/README.md에 source-of-truth 정책과 sync 절차를 문서화했습니다.review_followup.py는 public repo에 private workspace label UUID를 고정하지 않도록 라벨명을 기준으로 lookup/create 하게 정리했습니다.multica_agent_guard.py는 홈 디렉터리 절대경로 대신 runtime sibling path를 보도록 정리했습니다.🐙 Related Issue
Real Behavior Proof (필수)
ittae/.githubworktreepython3 -m unittest discover -s .github/tools/review_followup/tests -p 'test_*.py'python3 .github/tools/review_followup/sync_runtime_copy.py --runtime-dir "$(mktemp -d)" --dry-run --output jsonpython3 .github/tools/review_followup/sync_runtime_copy.py --runtime-dir "$(mktemp -d)" --output jsonRan 38 tests/OK~/.hermes/workspace/tools에 실적용 후 webhook end-to-end smokeHuman Decision Needed
필요하다면 설명: